package org.study.rule.boot.engine.spel;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.study.rule.boot.model.AlarmConfig;
import org.study.rule.boot.model.AlarmInfo;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 作用描述
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2024年08月25日
 */
@Slf4j
@UtilityClass
public class SpelUtils {
    /**
     * 根据告警条件、实际值判断是否触发告警。
     * 数据都是转为BigDecimal后再进行比较，因此只支持数值型告警配置
     *
     * @param alarmConfig 告警配置信息
     * @param value       实际值
     */
    public boolean hasAlarm(AlarmConfig alarmConfig, BigDecimal value) {
        log.info("当前告警配置：{}，实际值：{}", JSON.toJSONString(alarmConfig), value);
        boolean isAlarm = false;
        String config = alarmConfig.getConfig();
        if (StringUtils.isNotBlank(config)) {
            JSONObject jsonObject = JSONUtil.parseObj(config);
            String operator = jsonObject.getStr("operator");
            ExpressionParser parser = new SpelExpressionParser();
            StandardEvaluationContext context = new StandardEvaluationContext();
            switch (operator.toLowerCase()) {
                case "gt":
                    String gtParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(gtParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) > 0").getValue(context, Boolean.class));
                    break;
                case "ge":
                    String geParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(geParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) >= 0").getValue(context, Boolean.class));
                    break;
                case "lt":
                    String ltParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(ltParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) < 0").getValue(context, Boolean.class));
                    break;
                case "le":
                    String leParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(leParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) <= 0").getValue(context, Boolean.class));
                    break;
                case "eq":
                    String eqParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(eqParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) == 0").getValue(context, Boolean.class));
                    break;
                case "ne":
                    String neParam1 = jsonObject.getStr("param1");
                    // 设置变量供SpEL使用
                    context.setVariable("bigDecimal1", value);
                    context.setVariable("bigDecimal2", new BigDecimal(neParam1));
                    isAlarm = Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) != 0").getValue(context, Boolean.class));
                    break;
                case "in":
                    String inParam1 = jsonObject.getStr("param1");
                    String[] inSplit = inParam1.split(",");
                    for (String in : inSplit) {
                        // 设置变量供SpEL使用
                        context.setVariable("bigDecimal1", value);
                        context.setVariable("bigDecimal2", new BigDecimal(in.trim()));
                        if (Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) == 0").getValue(context, Boolean.class))) {
                            isAlarm = true;
                            break;
                        }
                    }
                    break;
                case "not_in":
                    String notInParam1 = jsonObject.getStr("param1");
                    String[] notInSplit = notInParam1.split(",");
                    boolean isNotIn = true;
                    for (String notIn : notInSplit) {
                        // 设置变量供SpEL使用
                        context.setVariable("bigDecimal1", value);
                        context.setVariable("bigDecimal2", new BigDecimal(notIn.trim()));
                        if (Boolean.TRUE.equals(parser.parseExpression("#bigDecimal1.compareTo(#bigDecimal2) == 0").getValue(context, Boolean.class))) {
                            isNotIn = false;
                            break;
                        }
                    }
                    if (isNotIn) {
                        isAlarm = true;
                    }
                    break;
                case "between":
                    String betweenParam1 = jsonObject.getStr("param1");
                    String betweenParam2 = jsonObject.getStr("param2");
                    // 设置变量供SpEL使用
                    context.setVariable("value", value);
                    context.setVariable("bigDecimal1", new BigDecimal(betweenParam1));
                    context.setVariable("bigDecimal2", new BigDecimal(betweenParam2));
                    boolean isGeBetweenParam1 = Boolean.TRUE.equals(parser.parseExpression("#value.compareTo(#bigDecimal1) >= 0").getValue(context, Boolean.class));
                    boolean isLeBetweenParam2 = Boolean.TRUE.equals(parser.parseExpression("#value.compareTo(#bigDecimal2) <= 0").getValue(context, Boolean.class));
                    isAlarm = (isGeBetweenParam1 && isLeBetweenParam2);
                    break;
                case "not_between":
                    String notBetweenParam1 = jsonObject.getStr("param1");
                    String notBetweenParam2 = jsonObject.getStr("param2");
                    // 设置变量供SpEL使用
                    context.setVariable("value", value);
                    context.setVariable("bigDecimal1", new BigDecimal(notBetweenParam1));
                    context.setVariable("bigDecimal2", new BigDecimal(notBetweenParam2));
                    boolean isLeNotBetweenParam1 = Boolean.TRUE.equals(parser.parseExpression("#value.compareTo(#bigDecimal1) < 0").getValue(context, Boolean.class));
                    boolean isGeNotBetweenParam2 = Boolean.TRUE.equals(parser.parseExpression("#value.compareTo(#bigDecimal2) > 0").getValue(context, Boolean.class));
                    isAlarm = (isLeNotBetweenParam1 || isGeNotBetweenParam2);
                    break;
                default:
                    log.error("暂时不支持该告警规则的解析：{}", JSON.toJSONString(alarmConfig));
                    break;
            }
        }
        return isAlarm;
    }

    /**
     * 根据告警条件、实际值判断是否触发告警，若有告警，则生成一条告警信息
     *
     * @param alarmConfig 告警配置信息
     * @param value       实际值
     */
    public AlarmInfo buildAlarmInfo(AlarmConfig alarmConfig, BigDecimal value) {
        log.info("当前告警配置：{}，实际值：{}", JSON.toJSONString(alarmConfig), value);
        AlarmInfo alarmInfo = null;
        if (hasAlarm(alarmConfig, value)) {
            alarmInfo = AlarmInfo.builder()
                    .name(alarmConfig.getName())
                    .level(alarmConfig.getLevel())
                    .deviceId(alarmConfig.getDeviceId())
                    .pointId(alarmConfig.getPointId())
                    .config(alarmConfig.getConfig())
                    .value(value.toPlainString())
                    .datetime(LocalDateTime.now())
                    .status(0)
                    .build();
            log.info("生成的告警信息为：{}", JSON.toJSONString(alarmInfo));
        } else {
            log.info("无告警");
        }
        return alarmInfo;
    }
}
